import store from '@/store'

let _URL = '' // 请求地址
let _TOKEN = '' // token值
let _SESSIONID = '' // 统计号
let _SOURCETYPE = '' // 来源类型
let _THIRDTOKEN = '' // 第三方token值
let _AGENTCODE = '' // 代理人编码
let ACTIONTYPE = '' // 事件类型
let AN = {}
let _threadTimer = null
let _STARTTIME // 进入页面时间戳
let _ENDTIME // 离开页面时间戳
let _DIFTIME // 浏览时间
let currentPageRule = {} // 页面配置
let _tArray = []

const _ajax = function ( options ) {
  var params = {
    url : '' ,
    type : 'get' ,
    data : {} ,
    success : function ( data ) {
    } ,
    error : function ( err ) {
    } ,
  }
  options = Object.assign ( params , options )
  if ( options.url ) {
    var xhr = XMLHttpRequest ? new XMLHttpRequest () : new ActiveXObject ( 'Microsoft.XMLHTTP' )
    var url = options.url ,
      type = options.type.toUpperCase () ,
      data = options.data ,
      dataArr = []
    //需要加随机数
    for ( let key in data ) {
      let value = key + '=' + data[ key ]
      dataArr.push ( value )
    }
    
    xhr.addEventListener ( "load" , function ( e ) {
      var resObj = null
      try {
        resObj = JSON.parse ( e.target.responseText )
        options.success ( resObj )
      } catch ( e ) {
        resObj = null
        console.log ( 'catch...' )
      }
      
    } , false )
    
    xhr.addEventListener ( "error" , function ( e ) {
      console.log ( 'error...' )
      console.log ( e )
    } , false )
    
    xhr.addEventListener ( "abort" , function () {
      console.log ( 'abort...' )
    } , false )
    
    if ( type === 'GET' ) {
      url = url + '?' + dataArr.join ( '&' )
      xhr.open ( type , url , true )
      xhr.send ()
    }
    
    if ( type === 'POST' ) {
      xhr.open ( type , url , true )
      xhr.setRequestHeader ( 'Content-type' , 'application/json' )
      xhr.send ( params.data ? JSON.stringify ( params.data ) : {} )
    }
  }
}
const _api = {
  collect ( {
    sessionId, // 本次统计流水号
    sourceType, // 来源类型 
    timestamp, // 时间戳
    token, // Token值
    thirdToken, // 第三方Token
    agentCode, // 代理人编码
    actionType, // 事件类型
    pageCode, // 页面编码
    pageName, // 页面名称
    elementCode, // 元素Code，确定唯一元素
    elementId, // 元素Id
    elementClass, // 元素Class
    elementValue, // 元素内容
    elementName, // 元素名称
    browseTime, // 浏览时长
    remark // 备注信息(扩展字段)
    } ) {
    return new Promise ( ( resolve ) => {
      _ajax ( {
        url: _URL,
        type : 'post' ,
        data : {
          sessionId: sessionId || _SESSIONID, // 本次统计流水号
          sourceType: sourceType || _SOURCETYPE, // 来源类型 
          timestamp: timestamp || (new Date()).getTime() + '', // 时间戳
          token: token || _TOKEN, // Token值
          thirdToken: thirdToken || _THIRDTOKEN, // 第三方Token
          agentCode: agentCode || _AGENTCODE, // 代理人编码
          actionType: actionType || ACTIONTYPE, // 事件类型
          pageCode: pageCode || currentPageRule.params.pageCode, // 页面编码
          pageName: pageName || currentPageRule.params.pageName, // 页面名称
          elementCode: elementCode || AN.elementCode, // 元素Code，确定唯一元素
          elementId: elementId || AN.elementId, // 元素Id
          elementClass: elementClass || AN.elementClass, // 元素Class
          elementValue: elementValue || AN.elementValue, // 元素内容
          elementName: elementName || AN.elementName, // 元素名称
          browseTime, // 浏览时长
          remark: JSON.stringify(remark) // 备注信息(扩展字段)
        } ,
        success : resolve ,
        error : resolve ,
      } )
    } )
  }
}

//分析组装节点
const _analysisNode = function ( path = [] , target ) {
  let tagName = _ElementUtil.getValidTagName ( target.tagName )
  let _an = []
  Array.prototype.map.call ( path , ( item ) => {
    const _node_attr = _getNodeAttr ( item )
    if ( currentPageRule && currentPageRule.element && currentPageRule.element.length > 0 ) {
      for ( let i = 0 ; i < currentPageRule.element.length ; i++ ) {
        const ar = currentPageRule.element[ i ]
        if ( new RegExp ( ar.elementCode ).test ( _node_attr[ 'elementcode' ] ) ) {
          _an.push ( { node : item,elementCode:ar.elementCode,attrs:_node_attr,rule:ar} )
        }
      }
    }
    return null
  } )
  console.log(_an)
  if ( !( _an.length > 0 && _an.find ( n => {
    if ( n ) return n
  } ) ) ) {
    return null
  }
  let params = {
    elementCode : '' ,
    elementId : '' ,
    elementClass : '' ,
    elementName: '',
    elementValue : '' ,
    click : false ,
    blur : false
  }
  let _anItem = _an.find ( n => {
    if ( n && n.elementCode ) return n
  } )
  console.log(_anItem)
  if ( _anItem ) {
    params.elementCode = _anItem.attrs.elementcode
    params.elementId = _anItem.attrs.id
    params.elementClass = _anItem.attrs.class
    params.elementName = _anItem.attrs.type
    params.click = _anItem.rule.click
    params.blur = _anItem.rule.blur
  }
  params.elementValue = new XMLSerializer ().serializeToString ( target )
  if ( tagName === 'input' || tagName === 'textarea' || tagName === 'select' ) {
    var value = target.value
    if ( target.type !== 'radio' && target.type !== 'checkbox' && value ) {
      params.elementValue = value
    } else if ( target.checked ) {
      params.elementValue = target.checked
    }
  }else if ( tagName === 'canvas' ) {
    params.elementValue = target.toDataURL ()
  }else if ( tagName === 'audio' || tagName === 'video' ) {
    params.elementValue = target.paused ? 'paused' : 'played'
  }else{
    var value = target.innerText
    params.elementValue = value
  }
  return objFilter(params)
}

const _getNodeAttr = function ( n ) {
  var _node_attr = {}
  if ( Object.prototype.toString.call ( n ) != "[object HTMLBodyElement]" && Object.prototype.toString.call ( n ) != "[object HTMLDocument]" && Object.prototype.toString.call ( n ) != "[object Window]" ) {
    for ( var _i = 0 , _a = Array.from ( n.attributes ) ; _i < _a.length ; _i++ ) {
      var _b = _a[ _i ] , name = _b.name , value = _b.value
      _node_attr[ name ] = _ElementUtil.transformAttribute ( document , name , value )
    }
  }
  return _node_attr
}

const _Queue = function () {
  let timer = null
  let _taskList = []
  this.push = function ( task ) {
    if ( Object.prototype.toString.call ( task ) === '[object Array]' ) {
      for ( const item of task ) {
        if ( Object.prototype.toString.call ( item ) === '[object Object]' ) {
          if ( item.hasOwnProperty ( 'callFunc' ) && Object.prototype.toString.call ( item[ 'callFunc' ] ) === '[object Function]' ) {
            _taskList.push ( item )
          }
        }
      }
    } else if ( Object.prototype.toString.call ( task ) === '[object Object]' ) {
      if ( task.hasOwnProperty ( 'callFunc' ) && Object.prototype.toString.call ( task[ 'callFunc' ] ) === '[object Function]' ) {
        _taskList.push ( task )
      }
    }
    if ( _taskList.length > 0 && !timer ) {//任务列表长度大于0并且没有定时器
      timer = setInterval ( () => {
        if ( _taskList.length <= 0 ) {//任务列表长度为0 清除计时器
          clearInterval ( timer )
          return timer = null
        }
        let task = _taskList.shift ()
        task.callFunc ( task.args )
      } , 0 )
    }
  }
}

const _ElementUtil = {
  syAndNumrRegex : RegExp ( '[^a-z1-6-]' ) ,
  URL_IN_CSS_REF : /url\((?:'([^']*)'|"([^"]*)"|([^)]*))\)/gm ,
  RELATIVE_PATH : /^(?!www\.|(?:http|ftp)s?:\/\/|[A-Za-z]:\\|\/\/).*/ ,
  DATA_URI : /^(data:)([\w\/\+\-]+);(charset=[\w-]+|base64).*,(.*)/i ,
  
  getValidTagName : function ( tagName ) {
    var processedTagName = tagName.toLowerCase ().trim ()
    if ( _ElementUtil.syAndNumrRegex.test ( processedTagName ) ) {
      return 'div'
    }
    return processedTagName
  } ,
  
  getUrlParam : function ( url , name ) {
    var paramIndex = url.indexOf ( '?' )
    var paramsUrl = paramIndex === -1 ? '' : url.slice ( paramIndex )
    if ( null !== paramsUrl && '' !== paramsUrl ) {
      if ( name ) {
        var reg = new RegExp ( "(^|&)" + name + "=([^&]*)(&|$)" )
        var r = paramsUrl.substr ( 1 ).match ( reg )
        if ( r !== null ) {
          return decodeURI ( r[ 2 ] )
        }
      } else {
        var theRequest = {}
        var strs = paramsUrl.split ( "&" )
        for ( var i = 0 ; i < strs.length ; i++ ) {
          theRequest[ strs[ i ].split ( "=" )[ 0 ] ] = ( strs[ i ].split ( "=" )[ 1 ] )
        }
        return theRequest
      }
    }
    return null
  } ,
  
  absoluteToStylesheet : function ( cssText , href ) {
    return ( cssText || '' ).replace ( _ElementUtil.URL_IN_CSS_REF , function ( origin , path1 , path2 , path3 ) {
      var filePath = path1 || path2 || path3
      if ( !filePath ) {
        return origin
      }
      if ( !_ElementUtil.RELATIVE_PATH.test ( filePath ) ) {
        return "url('" + filePath + "')"
      }
      if ( _ElementUtil.DATA_URI.test ( filePath ) ) {
        return "url(" + filePath + ")"
      }
      if ( filePath[ 0 ] === '/' ) {
        return "url('" + ( extractOrigin ( href ) + filePath ) + "')"
      }
      var stack = href.split ( '/' )
      var parts = filePath.split ( '/' )
      stack.pop ()
      for ( var _i = 0 , parts_1 = parts ; _i < parts_1.length ; _i++ ) {
        var part = parts_1[ _i ]
        if ( part === '.' ) {
          continue
        }
        else if ( part === '..' ) {
          stack.pop ()
        }
        else {
          stack.push ( part )
        }
      }
      return "url('" + stack.join ( '/' ) + "')"
    } )
  } ,
  
  getAbsoluteSrcsetString : function ( doc , attributeValue ) {
    if ( attributeValue.trim () === '' ) {
      return attributeValue
    }
    var srcsetValues = attributeValue.split ( ',' )
    var resultingSrcsetString = srcsetValues
      .map ( function ( srcItem ) {
        var trimmedSrcItem = srcItem.trimLeft ().trimRight ()
        var urlAndSize = trimmedSrcItem.split ( ' ' )
        if ( urlAndSize.length === 2 ) {
          var absUrl = absoluteToDoc ( doc , urlAndSize[ 0 ] )
          return absUrl + " " + urlAndSize[ 1 ]
        }
        else if ( urlAndSize.length === 1 ) {
          var absUrl = absoluteToDoc ( doc , urlAndSize[ 0 ] )
          return "" + absUrl
        }
        return ''
      } )
      .join ( ',' )
    return resultingSrcsetString
  } ,
  absoluteToDoc : function ( doc , attributeValue ) {
    if ( !attributeValue || attributeValue.trim () === '' ) {
      return attributeValue
    }
    var a = doc.createElement ( 'a' )
    a.href = attributeValue
    return a.href
  } ,
  isSVGElement : function ( el ) {
    return el.tagName === 'svg' || el instanceof SVGElement
  } ,
  
  transformAttribute : function ( doc , name , value ) {
    if ( name === 'src' || ( name === 'href' && value ) ) {
      return _ElementUtil.absoluteToDoc ( doc , value )
    }
    else if ( name === 'srcset' && value ) {
      return _ElementUtil.getAbsoluteSrcsetString ( doc , value )
    }
    else if ( name === 'style' && value ) {
      return _ElementUtil.absoluteToStylesheet ( value , location.href )
    }
    else {
      return value
    }
  }
  
}

const _EventUtil = {
  // 添加事件
  addHandler : function ( element , type , handler ) {
    if ( element.addEventListener ) {
      element.addEventListener ( type , handler , { capture : true } )
    } else if ( element.attachEvent ) {
      element.attachEvent ( "on" + type , handler )
    } else {
      element[ "on" + type ] = handler
    }
  } ,
  // 移除事件
  removeEvent : function ( element , type , handler ) {

    if ( element.removeEventListener ) {
      element.removeEventListener ( type , handler , { capture : true } )
    } else if ( element.datachEvent ) {
      element.detachEvent ( 'on' + type , handler )
    } else {
      element[ 'on' + type ] = null
    }
  } ,
  // 获取事件目标
  getTarget : function ( event ) {
    var event = event || window.event
    return event.target || event.srcElement
  } ,
  // 获取event对象的引用，取到事件的所有信息，确保随时能使用event；
  getEvent : function ( e ) {
    var ev = e || window.event
    if ( !ev ) {
      var c = this.getEvent.caller
      while ( c ) {
        ev = c.arguments[ 0 ]
        if ( ev && Event == ev.constructor ) {
          break
        }
        c = c.caller
      }
    }
    return ev
  },
  //修复 ios 上报拿不到path 问题
  getEventPath: function (event) { //EventUtil.getEventPath(e)
    return event.path || (event.composedPath && event.composedPath()) || null
  }
}

function _hasClass ( classNames , className ) {
  if ( !classNames ) return
  return classNames.split ( " " ).some ( n => {
    return new RegExp ( className ).test ( n )
  } )
}

//自定义上报
const dispatch = function ( data ) {
  console.log ( 'dispatch start' )
  let params = {
    actionType: data.actionType,
    elementCode: data.elementCode || '',
    elementId: data.elementId || '',
    elementClass: data.elementClass || '',
    elementValue: data.elementValue || '',
    elementName: data.elementName || '',
    remark: data.remark
  }
  //放入上报  直接发送
  _api.collect (params)
}

const _queue = new _Queue ()
const _pageHandler = function ( e , actionType ) {
  console.log(e, actionType)
  let n = e.target
  let tagName = _ElementUtil.getValidTagName ( n.tagName )
  let an = _analysisNode ( e.path , e.target )
  if ( !an ) return
  let params = {
    pageCode : currentPageRule.params.pageCode , //页面标识  没有走默认
    pageName : currentPageRule.params.pageName , //页面名称 没有走默认
    actionType , // 事件类型
    elementCode : an.elementCode , // 元素Code，确定唯一元素
    elementId: an.elementId, // 元素Id
    elementClass: an.elementClass, // 元素Class
    elementValue : an.elementValue , //上报事件触发的元素  页面级别：title）非页面级别为 元素内容   根据不通获取值不通
    elementName : tagName ,
    remark : null
  }
  let setData = function ( obj = {} ) {
    let keys = Object.keys ( obj )
    if ( keys.length <= 0 ) return
    for ( const key in obj ) {
      if ( obj.hasOwnProperty ( key ) ) {
        params[ key ] = obj[ key ]
      }
    }
  }
  //配置到  页面  级别
  if ( $_dot_push_before_fun.length > 0 ) {
    $_dot_push_before_fun.forEach ( ( e ) => {
      e.do ( $_dot_path_this , params )
    } )
  }
  if((params.actionType === 'CLICK' && an.click) || (params.actionType === 'BLUR' && an.blur)){
    ACTIONTYPE = params.actionType
    AN = an
    _queue.push ( { callFunc : _api.collect , args : params } )
  }
}
// 点击事件
const _clickHandler = function ( e ) {
  _pageHandler ( e , 'CLICK' )
}
// 失去焦点事件
const _blurHandler = function ( e ) {
  _pageHandler ( e , 'BLUR' )
}
const _errorHandler = function ( e ) {
  console.log ( 'errorPageHandler...' )
  console.log ( e )
}

// todo
//1.进入页面 事件，离开页面事件
//2.回调事件  配置级别
//3.失去焦点添加测试
//4.异常处理

//5.添加  token。thirdtoken 等
//6.参数配置到dev 环境
//7.初始化事件方式，和绑定优化
let $_dot_path_this = null
let $_dot_push_before_fun = []
const init = function ( url ) {
  // console.log ( "初始化" , url,getSessionId())
  // _APPID = appid
  _URL = url
  _SESSIONID = getSessionId()
  Vue.mixin ( {
    beforeRouteEnter ( to , from , next ) {
      //进入路由
      console.log ( '进入路由', to )
      if(to.query && to.query.agentCode) {
        _AGENTCODE = to.query.agentCode
      }
      _STARTTIME = (new Date()).getTime()
      //获取token、thirdToken
      _TOKEN = store.getters.getToken || ''
      // _TOKENTYPE = to.meta && to.meta.dot && to.meta.dot.params && to.meta.dot.params.tokenType
      _THIRDTOKEN = store.getters.getThirdToken || '' 
      if(to.meta && to.meta.dot && to.meta.dot.params && to.meta.dot.params.channelType && to.query[to.meta.dot.params.channelType]) {
        let channelType = to.meta.dot.params.channelType
        _SOURCETYPE = to.query[channelType].toUpperCase()
      } else {
        _SOURCETYPE = to.meta && to.meta.dot && to.meta.dot.params && to.meta.dot.params.sourceType
      }
      //刷新上报前置钩子和后置钩子
      $_dot_push_before_fun = []
      
      if ( to.meta && to.meta.dot && to.meta.dot.pushBefore && to.meta.dot.pushBefore.length > 0 ) {
        to.meta.dot.pushBefore.forEach ( ( e ) => {
          $_dot_push_before_fun.push ( require ( '@/dot/' + e ).default )
        } )
      }
      clearTimeout ( _threadTimer )
      if ( !( to.meta && to.meta.dot ) ) return next ()
      _threadTimer = setTimeout ( () => {
        //获取埋点配置
        currentPageRule = to.meta.dot
        //进入页面事件上报
        let params = {
          pageCode : currentPageRule.params.pageCode , //页面标识  没有走默认
          pageName : currentPageRule.params.pageName , //页面名称 没有走默认
          actionType : 'PAGELOAD' , //上报事件类型  进入，离开（页面级别）点击，获取（动作级别）
        }
        if(currentPageRule.load){
          ACTIONTYPE = 'PAGELOAD'
          _queue.push ( { callFunc : _api.collect , args : params } )
        }
        // 注册事件监听
        next ( vm => {
          $_dot_path_this = vm
          _EventUtil.addHandler ( document , 'click' , _clickHandler )
          _EventUtil.addHandler ( document , 'blur' , _blurHandler )
          _EventUtil.addHandler ( window , 'error' , _errorHandler )
          if ( currentPageRule.browseTime && currentPageRule.browseTime.length > 0 ) {
            _tArray.forEach((item)=>{
              clearTimeout(item)
            })
            currentPageRule.browseTime.forEach((item,key) => {
              let t
              let browseTime
              if(key == currentPageRule.browseTime.length -1) {
                let status = false
                t = setInterval ( () => {
                  status ? browseTime = item : browseTime = item - (key == 0 ? 0 : currentPageRule.browseTime[key-1])
                  status = true
                  //浏览时间上报
                  let params = {
                    pageCode : currentPageRule.params.pageCode , //页面标识  没有走默认
                    pageName : currentPageRule.params.pageName , //页面名称 没有走默认
                    actionType : 'BROWSEPAGE_' + item , //上报事件类型  进入，离开（页面级别）点击，获取（动作级别）
                    browseTime: browseTime + ''
                  }
                  ACTIONTYPE = 'BROWSEPAGE_' + item 
                  _queue.push ( { callFunc : _api.collect , args : params } )
                } , item * 1000 )
              } else {
                if(key == 0) {
                  browseTime = item
                } else {
                  browseTime = item - currentPageRule.browseTime[key-1]
                }
                t = setTimeout ( () => {
                  //浏览时间上报
                  let params = {
                    pageCode : currentPageRule.params.pageCode , //页面标识  没有走默认
                    pageName : currentPageRule.params.pageName , //页面名称 没有走默认
                    actionType : 'BROWSEPAGE_' + item , //上报事件类型  进入，离开（页面级别）点击，获取（动作级别）
                    browseTime: browseTime + '' // 浏览时间
                  }
                  ACTIONTYPE = 'BROWSEPAGE_' + item 
                  _queue.push ( { callFunc : _api.collect , args : params } )
                } , item * 1000 )
              }
              _tArray.push(t)
            })
          }
        } )
      } , 0 )
    } ,
    beforeRouteLeave ( to , from , next ) {
      //离开路由
      console.log ( '离开路由' )
      _tArray.forEach((item)=>{
        clearTimeout(item)
      })
      _ENDTIME = (new Date()).getTime()
      _DIFTIME = Math.round((_ENDTIME - _STARTTIME)/1000)
      let params = {
        pageCode : currentPageRule.params.pageCode , //页面标识  没有走默认
        pageName : currentPageRule.params.pageName , //页面名称 没有走默认
        browseTime : _DIFTIME,
        actionType : 'PAGEUNLOAD' , //上报事件类型  进入，离开（页面级别）点击，获取（动作级别）
      }
      if(currentPageRule.unload){
        ACTIONTYPE = 'PAGEUNLOAD' 
        AN = {}
        _queue.push ( { callFunc : _api.collect , args : params } )
      }
      
      //卸载监听
      _EventUtil.removeEvent ( document , 'click' , _clickHandler )
      _EventUtil.removeEvent ( document , 'blur' , _blurHandler )
      _EventUtil.removeEvent ( window , 'error' , _errorHandler )
      next ()
    }
  } )
}
// 生成uuid
function uuid() {
	var s = []
	var hexDigits = "0123456789abcdef"
	for (var i = 0; i < 36; i++) {
		s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1)
	}
	s[14] = "4"
	s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1)
	var uuid = s.join("")
	return uuid
}
// 获取sessionId
function getSessionId() {
  if(localStorage.getItem("_SESSIONID")) {
    return localStorage.getItem("_SESSIONID")
  } else if(sessionStorage.getItem("_SESSIONID")) {
    return sessionStorage.getItem("_SESSIONID")
  } else {
    var uuId = uuid()
    if(window.localStorage) {
      localStorage.setItem("_SESSIONID", uuId)
    } else {
      sessionStorage.setItem("_SESSIONID", uuId)
    }
    return uuId
  }
}
// 对象过滤空值
function objFilter(obj) {
  let newObj = {}
  for(let key in obj) {
    if(obj[key] === 0 || obj[key] || obj[key] === false) {
      newObj[key] = obj[key]
    }
  }
  return newObj
}
 
export default { 
  init , 
  dispatch , 
  // beforeCollect , 
  // afterCollect 
}
